package com.tx.studentManager.dao.base;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.transform.Transformers;

import com.tx.studentManager.common.QOAttr;
import com.tx.studentManager.model.BaseModel;
import com.tx.studentManager.model.Page;
import com.tx.studentManager.qo.BaseQo;


/**
 * 扩展分页查询和基于Criteria的条件查询
 * 
 * @author 此生唯晴
 * @param <T>
 * @param <PK>
 */
public abstract class HibernateGenericDao<T extends BaseModel, PK extends Serializable, QO extends BaseQo>
		extends BaseDao<T, PK> {

	/**
	 * 用于Dao层子类使用的构造函数. 通过子类的泛型定义取得对象类型Class.
	 * 
	 */
	public HibernateGenericDao() {
		super();
	}

	/**
	 * 用于省略Dao层, Service层直接使用通用GenericDao的构造函数. 在构造函数中定义对象类型Class.
	 * 
	 */
	public HibernateGenericDao(final SessionFactory sessionFactory,
			final Class<T> entityClass) {
		super(sessionFactory, entityClass);
	}

	/**
	 * 获取当前要查询的实体对象类型
	 * <p>
	 * 必须实现的抽象方法
	 * </p>
	 * 
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	protected Class getEntity() {
		return getEntityClass();
	}

	/**
	 * 子类中实现创建查询条件的对象
	 * 
	 * @param criteria
	 * @param qo
	 * 
	 * @return
	 */
	abstract protected Criteria buildCriteria(Criteria criteria, QO qo);

	/**
	 * 根据查询QO构建Criteria
	 * 
	 * @param qo
	 * @return
	 */
	private Criteria createBaseCriteria(QO qo) {
		Criteria criteria = getSession().createCriteria(getEntityClass());
		Field[] fields = qo.getClass().getDeclaredFields();
		// QO中字段名
		String fieldName = null;
		// QOAttr注解中name属性值
		String qoAttrName = null;
		// qo属性值
		Object fieldValue = null;
		for (Field field : fields) {
			fieldName = field.getName();

			if (field.getModifiers() > 16) {
				// final变量
				continue;
			}
			try {
				// 设置变量可访问
				field.setAccessible(true);
				fieldValue = field.get(qo);
				logger.debug("fieldName:" + fieldName + ",fieldValue:"
						+ fieldValue);
			} catch (IllegalArgumentException | IllegalAccessException e) {
				e.printStackTrace();
			}
			if (fieldValue != null) {
				//如果是字符型变量且内同空空，同样不添加条件
				if (fieldValue instanceof String ) {
					if (StringUtils.isBlank((String)fieldValue)) {
						continue;
					}
				}
				QOAttr annotation = field.getAnnotation(QOAttr.class);
				if (annotation != null) {
					qoAttrName = annotation.name() == null ? fieldName
							: annotation.name();
					logger.debug("annotation name:" + annotation.name());
					switch (annotation.type()) {
					case EQ:
						criteria.add(Restrictions.eq(qoAttrName, fieldValue));
						break;
					case GE:
						criteria.add(Restrictions.ge(qoAttrName, fieldValue));
						break;
					case GT:
						criteria.add(Restrictions.gt(qoAttrName, fieldValue));
						break;
					case LT:
						criteria.add(Restrictions.lt(qoAttrName, fieldValue));
						break;
					case LE:
						criteria.add(Restrictions.le(qoAttrName, fieldValue));
						break;
					case LIKE_ANY:
						criteria.add(Restrictions.like(qoAttrName,
								(String) fieldValue, MatchMode.ANYWHERE));
						break;
					case LIKE_END:
						criteria.add(Restrictions.like(qoAttrName,
								(String) fieldValue, MatchMode.END));
						break;
					case LIKE_START:
						criteria.add(Restrictions.like(qoAttrName,
								(String) fieldValue, MatchMode.START));
						break;
					case ORDER:
						Number orderVal = (Number) fieldValue;
						criteria.addOrder(orderVal.intValue() > 0 ? Order
								.asc(qoAttrName) : Order.desc(qoAttrName));
						break;
					case IN:
						if (fieldValue.getClass().isArray()) {
							Object[] valObjects = (Object[]) fieldValue;
							criteria.add(Restrictions
									.in(qoAttrName, valObjects));
						} else if (fieldValue instanceof Collection<?>) {
							@SuppressWarnings("unchecked")
							Collection<Object> valObjects = (Collection<Object>) fieldValue;
							criteria.add(Restrictions
									.in(qoAttrName, valObjects));
						}
						break;
					case FETCH_EAGER:
						if (fieldValue instanceof Boolean) {
							boolean fatchNow = (boolean) fieldValue;
							if (fatchNow) {
								criteria.setFetchMode(qoAttrName,
										FetchMode.JOIN);
							}
						}
						break;
					default:
						throw new IllegalArgumentException("type " + qoAttrName + " not supported.");
					}
				}
			}
		}
		buildCriteria(criteria, qo);
		return criteria;
	}

	/**
	 * 根据查询条件查询
	 * 
	 * @param qo
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<T> queryList(QO qo) {

		Criteria criteria = createBaseCriteria(qo);
		return (List<T>) criteria.list();
	}

	/**
	 * 根绝给定对象指定条件查询
	 * 
	 * @param entity
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<T> queryList(T entity) {
		Example example = Example.create(entity);
		example.excludeZeroes(); // 排除值为0的属性
		// example.excludeProperty("color"); // 排除 color属性
		example.ignoreCase(); // 忽略大小写
		example.enableLike(); // 启用模糊查询;
		return (List<T>) getSession().createCriteria(getEntityClass())
				.add(example).list();
	}

	/**
	 * 按Criteria查询唯一对象.
	 * 
	 * @param criterions
	 *            数量可变的Criterion.
	 */
	@SuppressWarnings("unchecked")
	public T findUnique(final Criterion... criterions) {
		return (T) createCriteria(criterions).uniqueResult();
	}

	/**
	 * 设置分页参数到Query对象,辅助函数.
	 */
	protected Query setPageParameterMap(final Query q, final Page page) {
		// hibernate的firstResult的序号从0开始
		if (page.getPageSize() > 0) {
			q.setFirstResult(page.getBeginIndex() - 1);
			q.setMaxResults(page.getPageSize());
		}
		return q;
	}

	/**
	 * 执行count查询获得本次SQL查询所能获得的对象总数.hql中不包含“select count(*)”。
	 * 
	 * <br/>
	 * 本函数只能自动处理简单的sql语句,复杂的sql查询请另行编写count语句查询.
	 * 
	 * @author 此生唯晴
	 */
	protected long countSqlResult(final String sql,
			final Map<String, Object> values) {
		String fromSql = sql;
		// select子句与order by子句会影响count查询,进行简单的排除.
		// ====================================================不支持Distinct形式的SQL语句
		// fromSql = "from " + StringUtils.substringAfter(fromSql, "from");
		// if (fromSql.indexOf("order by") > 0) {
		// fromSql = StringUtils.substringBefore(fromSql, "order by");
		// }
		// ==============================================================
		String countSql = "select count(*) from (" + fromSql + ")";

		try {
			Long count = (Long) createSQLQuery(countSql, values).uniqueResult();
			return count;
		} catch (Exception e) {
			throw new RuntimeException("can't auto count,sql is:" + countSql, e);
		}
	}

	/**
	 * 执行纯SQL查询
	 * 
	 * @param pageSize
	 *            页面尺寸大小,0表示不分页
	 * @param pageNo
	 *            当前页码，从1开始编号。
	 * @param sql
	 *            纯SQL命令
	 * @param clazz
	 *            封装到list中的对象的类型。
	 * @param param
	 *            参数
	 * @return 指定类对象列表。
	 */
	@SuppressWarnings("unchecked")
	public Page queryListByPage(Page page) {
		Object cond=page.getCondition();
		if (!(cond instanceof BaseQo)) {
			page.setCondition(null);
		}
		int totalCount=0;
		Criteria criteria=createBaseCriteria((QO) page.getCondition());
		//先保存Projection条件，后面查询总记录数会改变
		Projection projection=((CriteriaImpl)criteria).getProjection();
		Number number=(Number) criteria.setProjection(Projections.rowCount()).uniqueResult();
		if (number!=null) {
			totalCount=number.intValue();
		}
		logger.debug("[query by page]pageSize:"+page.getPageSize()+",fromIndex:"+page.getBeginIndex()+",totalCount:"+totalCount);
		if (totalCount==0) {
			page.setList(new ArrayList<T>());
			page.setPageCount(0);
			page.setTotalCount(0);
		}else {
			//重新设置查询条件
			criteria.setProjection(projection);
			criteria.setFirstResult(page.getBeginIndex());
			criteria.setMaxResults(page.getBeginIndex()+page.getPageSize());
			List<T> list=(List<T>) criteria.list();
			page.setList(list);
			page.setTotalCount(totalCount);
		}
		return page;
	}

	/**
	 * [findPageWithOR] 方法 调用：捆绑查询条件
	 * 
	 * @param criteria
	 * @param criterions
	 * @return
	 */
	public Criteria boundCriterion(Criteria criteria,
			final Criterion... criterions) {
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}

	/**
	 * 在hibernate中使对象处于脱管状态
	 * 
	 */
	public void evictEntity(final T entity) {
		if (entity != null) {
			getSession().evict(entity);
		}
	}

	/**
	 * 根据sql返回一个实体
	 * <p>
	 * 有可能查到多个，但，只取查到的第一个
	 * </p>
	 */
	@SuppressWarnings("unchecked")
	public T findSQLNonHibernateEntity(final String sql,
			final Map<String, Object> values) {
		Query query = createSQLQuery(sql, values).setResultTransformer(
				Transformers.aliasToBean(getEntityClass()));
		query.setMaxResults(1);
		return (T) query.uniqueResult();
	}
}